iT邦幫忙

2023 iThome 鐵人賽

DAY 19
1
Cloud Native

時光之鏡:透視過去、現在與未來的 Observability系列 第 19

OpenTelemetry SDK — Automatic Instrumentation 給你一對翅膀

  • 分享至 

  • xImage
  •  
資訊處理流程 生成 收集 儲存 使用
OpenTelemetry SDK

在 OpenTelemetry 的官網上,上面介紹著 「OpenTelemetry is a collection of APIs, SDKs, and tools.」,本篇將簡單介紹如何透過 OpenTelemetry 的 SDK 來生成 Telemetry Data。

OpenTelemetry 支援多種程式語言的 SDK,使開發者能迅速將其整合至現有系統,並將資料傳送至不同的 Tracing System 作為儲存 Trace 資訊的 Tracing Backend。使用 OpenTelemetry SDK 是一種基本的整合方式,需要開發者在程式碼中手動添加 Trace 資訊和其他相關細節,但這種方式需要開發者對 OpenTelemetry 的 API 有一定的了解。

為了讓開發者能再更輕鬆整合 OpenTelemetry,OpenTelemetry 提供了 InstrumentationInstrumentation一般翻譯為「儀器化」。在軟體工程領域,這是一種用來測量服務效能、行為以及其他相關資訊的機制。OpenTelemetry 提供的 Instrumentation 分為兩種:

  1. Manual Instrumentation:手動設定,相對於使用 SDK 更為簡單些,但仍需自行調整程式碼。
  2. Automatic Instrumentation:無需手動調整程式碼,直接搭配特定語言或框架即可自動生成 Telemetry Data。支援的語言包括 .NET、Java、JavaScript、PHP 和 Python。

由於 Automatic Instrumentation 功能強大且簡單易用,本篇會重點介紹這方面的使用方式。

Automatic Instrumentation

使用 Automatic Instrumentation 時,簡單的資料流向如下:

  1. OpenTelemetry Automatic Instrumentation 會自動生成 Telemetry Data,並將資料送出。
  2. Tracing Backend(例如:Zipkin、Jaeger、Tempo)負責收集和儲存這些資料。
  3. 使用者可透過對應的 UI(例如:Zipkin UI、Jaeger UI、Grafana)查詢和分析 Telemetry Data。

Pipeline
APP A 與 APP B 配有 Automatic Instrumentation,以 Filter 或 Middleware 的方式罩住 Application,會自動判斷接收到的 Request 有沒有 Trace ID,若有就沿用,沒有就生成新的Trace ID。

  1. User 發送 Request 給 APP A 後,因為還沒有 Trace ID,所以生成 Trace ID 123
  2. 該筆請求還會由 APP A 再發送一個給 APP B 的 Request,該 Request 也會被 Automatic Instrumentation 加上 Trace ID 123
  3. APP B 的 Automatic Instrumentation 發現已有 Trace ID 123,所以沿用
  4. APP B 再以同樣邏輯發送一個 Request 給 APP C
  5. 因為 APP C 沒有 Automatic Instrumentation,所以不會收集到 APP C 相關的 Span

Config

Automatic Instrumentation 的設定方式因不同語言和框架而有所不同,但通常透過環境變數或設定檔來完成。以環境變數為例,以下是一些常用的參數:

  1. OTEL_TRACES_EXPORTER:設定 Trace 資料傳送的方式,例如 otlpjaeger
    1. OTEL_EXPORTER_OTLP_ENDPOINT:使用 OTLP 時,搭配的 Endpoint 參數,例如 http://localhost:4317
    2. OTEL_EXPORTER_JAEGER_ENDPOINT:使用 Jaeger 時,搭配的 Endpoint 參數,例如 http://localhost:14250
  2. Service Name:用於 Span 中辨別不同服務,例如 service-a,有兩種方式可以設定:
    1. OTEL_SERVICE_NAME:設定服務名稱,例如 service-a
    2. OTEL_RESOURCE_ATTRIBUTES:透過增加資源屬性來設定 service.name,例如 service.name=service-a
  3. OTEL_PROPAGATORS:設定用以傳遞 Tracing Header 的方式,可設置多個,用 , 分隔,預設為 tracecontext,baggage。通常用於跟其他 Tracing System 整合時使用,例如 OTEL_PROPAGATORS=tracecontext,baggage,jaeger,詳細資訊可參考 Propagators

目前,最完整的環境參數設定說明是 Java 的 OpenTelemetry SDK Autoconfigure 文件。由於這些設定方式通常是跨語言的,因此也可以參考 Java 的設定來應用在其他語言。

Logging with OpenTelemetry

在多執行緒的應用程式中,不同執行緒所產生的 Log 可能會交錯,使得難以追蹤問題。透過 OpenTelemetry 的 SDK 和 Log Pattern 的調整,我們可以將 Trace ID 加入到日誌中。這樣一來,問題排除就能從 Trace 開始,接著根據 Log 中的 Trace ID 定位具體的問題點。

範例 Log 如下:

[2021-10-13 10:00:00,000] [INFO] [trace_id=123] [class=Main] [message=Hello World]
[2021-10-13 10:00:01,000] [INFO] [trace_id=456] [class=Worker] [message=Hello World]
[2021-10-13 10:00:02,000] [INFO] [trace_id=456] [class=Worker] [message=Hello World]
[2021-10-13 10:00:03,000] [INFO] [trace_id=123] [class=Worker] [message=Hello World]

不同程式語言有其特定的 Log 搭配機制。例如,Python 使用 OpenTelemetry Logging Instrumentation,而 Java 則是配合 Log4j 和 Logback 使用 Logger MDC auto-instrumentationMDC 機制。

Python Automatic Instrumentation

在 Python 環境下,使用 Automatic Instrumentation 至少需要安裝以下三個主要套件:

  1. opentelemetry-distro: 這個套件會同時安裝 OpenTelemetry 的 APISDKInstrumentation
  2. opentelemetry-exporter: 根據使用的 Tracing Backend,需要安裝相對應的 Protocol Exporter。例如 OpenTelemetry Protocol 的 opentelemetry-exporter-otlp,Jaeger 的 opentelemetry-exporter-jaeger
  3. opentelemetry-instrumentation: 針對套件或是框架設計的 Instrumentation Library,Automatic Instrumentation 啟動後就會自動偵測、添加 Trace ID 與生成 Telemetry Data 等
    1. 例如 Flask 的 opentelemetry-instrumentation-flask,FastAPI 的 opentelemetry-instrumentation-fastapi,requests 的 opentelemetry-instrumentation-requests
    2. 或者也可以直接使用安裝 opentelemetry-instrumentation 附帶的 opentelemetry-bootstrap 指令快速安裝對應的 Instrumentation Library,執行 opentelemetry-bootstrap -a install 時會偵測環境已安裝的其他套件,並自動安裝對應的 Instrumentation Library

套件都安裝完畢後,啟動 Automatic Instrumentation 的方式為使用 opentelemetry-instrument 指令帶起 Python 的程式,例如:

opentelemetry-instrument python main.py

共有兩種方式可以設定 OpenTelemetry Instrumentation 的參數:

  1. Environment Variables
  2. opentelemetry-instrument 的 CLI arguments

以設定 OTLP Endpoint 為 http://localhost:4317 的情境為例:

# 使用 Environment Variables
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317 opentelemetry-instrument python main.py

# 使用 CLI arguments
opentelemetry-instrument --exporter_otlp_endpoint http://localhost:4317 python main.py

目前針對 Python 的文件沒有提供完整的參數清單,但可以透過 opentelemetry-instrument --help 指令來查看。

Java

Java 的 Automatic Instrumentation 只支援 Java 8 以上的版本與特定的工具,支援的 Library、Framework、Application Server 與 JVM 可以參考 官方文件。使用 Automatic Instrumentation 後,會自動捕捉進出該服務的所有 Request,並產生相對應的 Telemetry Data,包括了對服務的 Request、對外部服務發送的 Request、資料庫操作等。

啟動 Java 的 Automatic Instrumentation 只需要在執行時使用 -javaagent 參數指定 OpenTelemetry Instrumentation for Java 的 agent jar 檔:

java -javaagent:path/to/opentelemetry-javaagent.jar -jar myapp.jar

Agent jar 檔可以在 Repo 的 Releases Assets 下載,或者直接下載最新版

共有四種方式可以設定 OpenTelemetry Agent 的參數,完整的參數清單可參考官方文件,優先順序由高到低:

  1. System Properties
  2. Environment Variables
  3. Configuration File
  4. ConfigPropertySource SPI

以設定 OTLP Endpoint 為 http://localhost:4317 為例,三種常見的設定方式如下:

  1. System Properties

    java -javaagent:path/to/opentelemetry-javaagent.jar \
         -Dotel.exporter.otlp.endpoint=http://localhost:4317 \
         -jar myapp.jar
    
  2. Environment Variables

    OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317 java -javaagent:path/to/opentelemetry-javaagent.jar -jar myapp.jar
    
  3. Configuration File

    # path/to/otel.properties
    otel.exporter.otlp.endpoint=http://localhost:4317
    
    java -javaagent:path/to/opentelemetry-javaagent.jar \
         -Dotel.javaagent.configuration-file=path/to/otel.properties \
         -jar myapp.jar
    

Lab

範例程式碼:19-otel-sdk

Quick Start

  1. 啟動所有服務

    docker-compose up -d
    
  2. 檢視服務

    1. FastAPI App
      1. app-a: http://localhost:8000
      2. app-b: http://localhost:8001
      3. 可透過 docker logs -f app-adocker logs -f app-b 檢視 Log 中的 Trace ID
    2. Spring Boot App
      1. app-c: http://localhost:8002
      2. 可透過 docker logs -f app-c 檢視 Log 中的 Trace ID
    3. Grafana: http://localhost:3000,登入帳號密碼為 admin/admin
      1. 點擊左上 Menu > Explore,左上 Data Source 選擇 Tempo,即可看到 Tempo 收集的 Traces
      2. 透過瀏覽器對 application 的 /chain 發送 Request,可以在 Trace 資訊中看到 app-aapp-bapp-c 互相呼叫的順序
        1. app-a: http://localhost:8000/chain
        2. app-b: http://localhost:8001/chain
        3. app-c: http://localhost:8002/chain
  3. 關閉所有服務

    docker-compose down
    

Goals

  1. 建立 FastAPI App(app-a、app-b),透過 OpenTelemetry Automatic Instrumentation 產生與收集 Traces,並發送至 Tempo
  2. 建立 Spring Boot App(app-c),透過 OpenTelemetry Automatic Instrumentation 產生與收集 Traces,並發送至 Tempo
  3. 建立 Tempo,接收 Traces 資料
  4. 建立 Grafana,查詢 Tempo 顯示 Traces 資料

小結

想要迅速掌握 OpenTelemetry,最有效的方法是透過一個 Demo Project 來直接體驗其功能。除了本篇 Lab 所介紹的 Python 和 Java 範例,OpenTelemetry 官方也推出了一個非常完整的 Demo Project,包含了 Docker 和 Kubernetes 兩個版本,可以直接使用。在學習了 Automatic Instrumentation 的基本使用方法後,也可以更進一步地自定義 OpenTelemetry 的使用。例如,可以利用 SDK 在 Span 中加入 Automatic Instrumentation 未提供的 Attribute,如 IP 或使用者資訊等。

參考資料

  1. OpenTelemetry Documentation
  2. OpenTelemetry開箱即用 即時監控分散式系統效能

上一篇
Traces — 看系統,一個兩個三個四個,連成線
下一篇
Tempo - 小孩才做選擇,Trace 我全都要
系列文
時光之鏡:透視過去、現在與未來的 Observability30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0

您好,有兩個問題想要請教:

  1. 想請問 OpenTelemetry SDK 和 prometheus exporter 的功能是不是差不多呢?
    我的情境是想要在希望能動越少 production 的 code,但看起來使用 OpenTelemetry 或是 exporter 都需要安裝在該台伺服器才能收集都資訊,也想詢問哪個方法比較推薦。

  2. 如果架設一台伺服器專門收集不同台的 server API 資訊,是否建議呢?會需要各 server 在 call api 的時候都打這一台 server 收集 API 資訊。

看更多先前的回應...收起先前的回應...
Blueswen iT邦新手 3 級 ‧ 2024-01-04 21:36:33 檢舉
  1. OpenTelemetry SDK 讓 Application 主動送出可觀測性資訊,文章中主要只介紹 Trace;Prometheus Exporter 是獨立的 Process 去收集相關的 Metrics,從 Application 的角度來看是被動被收集。如果是想要盡可能不修改程式碼,可以透過 Automatic Insturmentation 的方式收集 Trace,再搭配後面文章有介紹到的 Span Metrics 產生 Metrics。
  2. 要收集哪些 API 資訊呢?這邊提到的 Server 是提供服務的機器,還是發送 Request 的機器呢?

作者您好:

由於公司有多台 Server,且不同台 Server 語言環境也不盡相同。因此想在不動 legacy code 的情況下去增加監控系統。

  1. 了解,後續我會試試看 Automatic Insturmentation!
    這裡回饋一下,我之前是將 FastAPI 導入 exporter 嘗試看看 https://github.com/trallnag/prometheus-fastapi-instrumentator 並生成 Grafana
    https://ithelp.ithome.com.tw/upload/images/20240105/20114380pUFT6Dw6fk.png

我有嘗試您的 https://github.com/blueswen/observability-workshop-101/tree/main/lab-5
https://ithelp.ithome.com.tw/upload/images/20240105/20114380Fov1RZDYUv.png

感覺大同小異,因此有點好奇 exporter 和 OpenTelemetry 在 API 層級應該都是提供差不多資訊。就我理解,exporter 是有系統資訊 + API,而 OpenTelemetry 是API + tracing。不知道這樣理解是否正確?

  1. 我想要在各 Server 端新增監控 API 層,但不太希望動到原始環境的程式碼,於是想說在被監控的 Server 的 API 都裝飾上另一個監控 API。這支 API 專門捕捉並記錄相關的監控資料給中央 Server,並提供 /metrics 給後面的 prometheus。好奇是否能做到類似 OpenTelemetry 或是 exporter 的效果呢?(會不會其實沒辦法跟 OpenTelemetry 和 exporter 拿到的指標一樣多,有點繞遠路。)
    https://ithelp.ithome.com.tw/upload/images/20240105/20114380oK1JdtpKC0.png

若有空再麻煩您回覆,謝謝!

Blueswen iT邦新手 3 級 ‧ 2024-01-19 00:42:00 檢舉
  1. 前面我說的 Expoter 指的是 https://prometheus.io/docs/instrumenting/exporters/ ,你使用的 prometheus-fastapi-instrumentator 其實應該歸類為 Client Libraries
    1. 就我的理解一般的 Client Libraries(也就是你用的 prometheus-fastapi-instrumentator) 都是收集跟 Application 相關的 Metircs,可能包含 Request 相關的指標、使用多少記憶體、運行了多久等,但並不會包含系統(機器)資訊,例如磁碟空間、CPU 使用率、網路狀況等,這些系統資訊一般都是另外透過 exporter 採集。
    2. OpenTelemetry 涵蓋範圍很廣,精準一點描述的話在這篇裡面介紹的是用 OpenTelemetry Automatic Instrumentation 讓 Application 生成並發送 Trace 資訊至 Trace Backend 儲存而已。而 API Metrics 則是需要再透過額外的 OpenTelemetry Collector 分析 Trace 資訊解析出來,所以只會有 Request 相關的指標,不會有更多關於 Application 本身的 Metrics。
    3. 簡單來講你用的 prometheus-fastapi-instrumentator 是提供 Application Metrics;OpenTelemetry Automatic Instrumentation 是產生 Trace,Application 的 Request Metircs 需要再透過 OpenTelemetry Collector 生成。
  2. 不太確定如果你不想要異動現有的程式碼的話,要怎麼達成「在被監控的 Server 的 API 都裝飾上另一個監控 API」?不過如果從異動的程度來分析的話,可以有以下幾個方法讓你取得更多 Metrics:
    1. 可以直接改動程式碼內容:那就可以直接使用 prometheus-fastapi-instrumentator 這類 Client Libraries,這樣取得的 Metircs 是最豐富的,收集的話就是 Prometheus 訂 Job 去每一個服務的 Metrics Endpoint 採集。
    2. 不能改動程式碼,但可以改動程式的啟動方式與異動執行環境的套件:使用 OpenTelemetry Automatic Instrumentation 產生 Trace,例如像是在這篇 python 用的 opentelemetry-instrument,再串接 OpenTelemetry Collector 利用其 SpanMetrics 的功能計算出 Request 相關的 Metrics,Metircs 的採集由 Prometheus 去 OpenTelemetry Collector 的 Metircs Endpoint 爬取。詳細作法與說明可以參考我的另外一個 Project https://github.com/blueswen/opentelemetry-apm
    3. 程式內容跟運行環境全部都不能動:在 Application Server 前面多掛一台 Proxy,紀錄每個 Reqesut 的資訊轉換成 Metric,這個作法不太確定有沒有現成的工具,可能要再找找看,概念跟 Istio 在 Kiali 看服務間的 RPS、Error Rate 很像。

非常感謝您的說明,後續我以您提供的方法2,架設 opentelemetry-instrument 並串接 OpenTelemetry Collector 來生成相關 Metrics。

不過仍有一個問題是,如果還是想要紀錄 CPU 和 Memory 等等資訊,應該是仍要透過 exporter。目前實作 OpenTelemetry 似乎沒有辦法記錄到系統相關資訊,僅能抓 API。

參考您提供的 連結,在實作上遇到一些小問題:

1.發現:
OpenTelemetry Collector 的 Metrics 不會紀錄 /error_test 所產生的 http_status_code,而是生成
status_code="STATUS_CODE_ERROR"
並且此時 error 被紀錄是 span_kind="SPAN_KIND_SERVER" 而非不是 span_kind="SPAN_KIND_INTERNAL"

https://ithelp.ithome.com.tw/upload/images/20240125/20114380n7n51oL5d0.png

https://ithelp.ithome.com.tw/upload/images/20240125/20114380WrDW6XsHqc.png
2. Grafana 儀表板:

因此在儀表板不會特別顯示 exception 500 的相關 error
(但如果是自己 raise HttpException 500 這個就會有)
https://ithelp.ithome.com.tw/upload/images/20240125/20114380gxnKKLoGpy.png

因此,如果想要將系統送出的 500 error 也抓住,讓 OTEL 生成 http_status_code=500,想請教這一塊有什麼建議作法嗎?

Blueswen iT邦新手 3 級 ‧ 2024-02-04 21:29:11 檢舉

應該是因為 FastAPI 的 Automatic Instumentaion 本身設計的關係,當沒有以正常的流程回傳時,會無法捕捉到回傳的 Http Status Code,如下圖 Span 中的 Attribute:
Span Data

如果希望這類沒有辦法解析出 http_status_code 的 trace,也能有 http_status_code 的 label,可以參考 Span Metrics 的 文件,將 http_status_code 這個 dimension 加上 default 值,例如:

connectors:
  spanmetrics:
    dimensions:
      - name: http.method
      - name: http.status_code
        default: 500
      - name: http.route

我要留言

立即登入留言